package com.raft.core.log;

import java.util.List;
import java.util.Set;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.raft.core.log.entry.AddNodeEntry;
import com.raft.core.log.entry.Entry;
import com.raft.core.log.entry.EntryMeta;
import com.raft.core.log.entry.GeneralEntry;
import com.raft.core.log.entry.GroupConfigEntry;
import com.raft.core.log.entry.NoOpEntry;
import com.raft.core.log.entry.RemoveNodeEntry;
import com.raft.core.log.statemachine.StateMachine;
import com.raft.core.node.NodeEndpoint;
import com.raft.core.node.NodeId;
import com.raft.core.rpc.message.AppendEntriesRpc;
import com.raft.core.rpc.message.InstallSnapshotRpc;

/**
 * Log.
 *
 * @see com.raft.core.log.sequence.EntrySequence
 * @see com.raft.core.log.snapshot.Snapshot
 */
public interface Log {

    int ALL_ENTRIES = -1;

    /**获取最后一条日志的元信息，应用场景：用于选举开始时、发送消息时(RequestVote)
     * <br/>Get meta of last entry.
     *
     * @return entry meta
     */
    @Nonnull
    EntryMeta getLastEntryMeta();

    /**创建AppendEntries消息，应用场景：用于Leader向Follower发送日志复制消息时
     * <br/>Create append entries rpc from log.
     *
     * @param term       current term 当前(Leader)节点的term
     * @param selfId     self node id 当前(Leader)节点
     * @param nextIndex  next index 每个member当前的next index，如果是刚被选出来Leader，则Leader进行重置后，所有member的next index就是Leader的next index
     * @param maxEntries max entries 最大读取的日志条数。raft算法中没有提及AppendEntries消息中日志条目的数量，但是假如传输全部日志条目，有可能会导致网络拥堵。为了限制日志条目数量，
     * <br/>增加了maxEntries参数。maxEntries默认为-1，表示传输从nextLog到最后的全部日志条目
     * @return append entries rpc
     */
    AppendEntriesRpc createAppendEntriesRpc(int term, NodeId selfId, int nextIndex, int maxEntries);

    /**
     * Create install snapshot rpc from log.
     *
     * @param term   current term
     * @param selfId self node id
     * @param offset data offset
     * @param length data length
     * @return install snapshot rpc
     */
    InstallSnapshotRpc createInstallSnapshotRpc(int term, NodeId selfId, int offset, int length);

    /**
     * Get last uncommitted group config entry.
     *
     * @return last committed group config entry, maybe {@code null}
     */
    @Nullable
    GroupConfigEntry getLastUncommittedGroupConfigEntry();

    /**获取下一条日志的索引，应用场景：在节点成为leader服务器时，leader服务器需要重置Follower服务器的日志复制进度，此时所有Follower服务器的初始nextLogIndex都是当前服务器下一条日志的索引
     * <br/>Get next log index.
     *
     * @return next log index
     */
    int getNextIndex();

    /**获取当前的CommitIndex
     * Get commit index.
     *
     * @return commit index
     */
    int getCommitIndex();

    /**判断对象的lastLogIndex和lastLogTerm是否比自己新，应用场景：收到RequestVote消息时，选择是否投票时使用的方法
     * <br/>Test if last log self is new than last log of leader.
     *
     * @param lastLogIndex last log index
     * @param lastLogTerm  last log term
     * @return true if last log self is newer than last log of leader, otherwise false
     */
    boolean isNewerThan(int lastLogIndex, int lastLogTerm);

    /**增加一个no-op日志，应用场景：当前节点成为leader后的第一条no-op日志
     * <br/>Append a NO-OP log entry.
     *
     * @param term current term
     * @return no-op entry
     */
    NoOpEntry appendEntry(int term);

    /**增加一条普通日志，应用场景：上层服务操作
     * <br/>Append a general log entry.
     *
     * @param term    current term
     * @param command command in bytes
     * @return general entry
     */
    GeneralEntry appendEntry(int term, byte[] command);

    /**
     * Append a log entry for adding node.
     *
     * @param term            current term
     * @param nodeEndpoints   current node configs
     * @param newNodeEndpoint new node config
     * @return add node entry
     */
    AddNodeEntry appendEntryForAddNode(int term, Set<NodeEndpoint> nodeEndpoints, NodeEndpoint newNodeEndpoint);

    /**
     * Append a log entry for removing node.
     *
     * @param term          current term
     * @param nodeEndpoints current node configs
     * @param nodeToRemove  node to remove
     * @return remove node entry
     */
    RemoveNodeEntry appendEntryForRemoveNode(int term, Set<NodeEndpoint> nodeEndpoints, NodeId nodeToRemove);

    /**追加来自leader的日志条目，应用场景：收到来自leader服务器的日志复制请求时
     * <br/>Append entries to log.
     *
     * @param prevLogIndex expected index of previous log entry
     * @param prevLogTerm  expected term of previous log entry
     * @param entries      entries to append
     * @return true if success, false if previous log check failed
     */
    boolean appendEntriesFromLeader(int prevLogIndex, int prevLogTerm, List<Entry> entries);

    /**推进commitIndex，应用场景：收到来自leader服务器的日志复制请求时
     * <br/>Advance commit index.
     *
     * <p>
     * The log entry with new commit index must be the same term as the one in parameter,
     * otherwise commit index will not change.
     * </p>
     *
     * @param newCommitIndex new commit index
     * @param currentTerm    current term
     */
    void advanceCommitIndex(int newCommitIndex, int currentTerm);

    /**安装日志快照
     *
     * @param rpc rpc
     * @return install snapshot state
     */
    InstallSnapshotState installSnapshot(InstallSnapshotRpc rpc);

    /**
     * Generate snapshot.
     *
     * @param lastIncludedIndex last included index
     * @param groupConfig       group config
     */
    void generateSnapshot(int lastIncludedIndex, Set<NodeEndpoint> groupConfig);

    /**
     * Set state machine.
     * <p>
     * It will be called when
     * <ul>
     * <li>apply the log entry</li>
     * <li>generate snapshot</li>
     * <li>apply snapshot</li>
     * </ul>
     *
     * @param stateMachine state machine
     */
    void setStateMachine(StateMachine stateMachine);

    StateMachine getStateMachine();

    /**关闭
     * <br/>Close log files.
     */
    void close();

}
